package cn.skylark.wcm.common.jwt;

import cn.skylark.wcm.common.redis.RedisUtils;
import cn.skylark.wcm.common.util.PasswordHelper;
import cn.skylark.wcm.entity.User;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JwtUtil
 *
 * @author chuhl
 * @date 2018/1/10
 */
public class JwtUtil {
    private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
    /**
     * 过期时间（1小时）
     */
    public static final long EXPIRATION_TIME = 3600_000;

    /**
     * 加入加密附加字符
     */
    public static final String SECRET = "ThisIsASecret";
    /**
     * TOKEN前缀
     */
    public static final String TOKEN_PREFIX = "Bearer";
    /**
     * TOKEN保存位置
     */
    public static final String HEADER_STRING = "Authorization";
    /**
     * TOKEN作成
     *
     * 1:获取用户实体信息
     * 2:对用户名,用户ID进行加密处理
     * 3:调用JWT生成TOKEN
     * 4:返回TOKEN
     *
     * @param user 用户实体
     * @return 返回生成TOKEN
     */
    public String generateToken(User user) throws Exception {
        logger.info( "生成TOKEN处理开始--username="+user.getUsername());
        HashMap<String, Object> map = new HashMap<>();
        PasswordHelper passwordHelper = new PasswordHelper();
        map.put("username", passwordHelper.encrypt( user.getUsername()));
        map.put("userid", passwordHelper.encrypt( String.valueOf( user.getId() )));
        String jwt = Jwts.builder()
                .setSubject( user.getUsername() )
                .setClaims(map)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith( SignatureAlgorithm.HS512, SECRET)
                .compact();
        logger.info( "生成TOKEN处理结束");
        return jwt;
    }

    /**
     * 验证TOKEN
     * 1:通过request请求获取客户端TOKEN
     * 2:客户端TOKEN为空，则验证结果417（过期）
     * 3:客户端TOKEN不为空，则调用JWT，解析客户端TOKEN字符串，获取用户名
     * 4:获取客户端请求SESSIONID，调用REDIS实例，获取REDIS缓存服务器中存储的REDISTOKEN字符串，调用JWT解析，获取用户名
     * 5:比较客户端用户名和REDIS用户名，一致返回结果200，不一致返回结果401，发生例外则返回403
     *
     * @param request request请求
     * @param redisUtils REDIS实例
     * @return 返回验证结果
     */
    public Map<String ,Object> validateToken(HttpServletRequest request, RedisUtils redisUtils){
        logger.info( "TOKEN验证处理开始");
        String requestToken = request.getHeader(HEADER_STRING);
        Map<String ,Object> retMap = new HashMap<>(  );
        //请求SESSIONID
        String sessionId = request.getSession().getId();
        PasswordHelper passwordHelper = new PasswordHelper();
        if (requestToken != null) {
            try {
                //前端请求TOKEN解析
                Map<String, Object> requestbody = Jwts.parser()
                        .setSigningKey( SECRET )
                        .parseClaimsJws( requestToken.replace( TOKEN_PREFIX, "" ) )
                        .getBody();

                //获取前端请求TOKEN中的USERNAME
                String requestusername = passwordHelper.decrypt( (String) requestbody.get( "username" ) );
                String requestuserid = passwordHelper.decrypt( (String) requestbody.get( "userid" ) );

                //获取REDIS缓存服务器中的TOKEN信息
                String redisToken = (String) redisUtils.get( "token|" + sessionId );
                String redisusername = "";
                String redisuserid = "";

                if (redisToken != null) {
                    //解析TOKEN信息
                    Map<String, Object> redisbody = Jwts.parser()
                            .setSigningKey( SECRET )
                            .parseClaimsJws( redisToken.replace( TOKEN_PREFIX, "" ) )
                            .getBody();
                    //获取REDIS服务器上的TOKEN解析信息中的USERNAME
                    redisusername = passwordHelper.decrypt( (String) redisbody.get( "username" ) );
                    redisuserid = passwordHelper.decrypt( (String) redisbody.get( "userid" ) );
                }
                //进行验证
                if (requestusername.equals( redisusername ) && requestuserid.equals( redisuserid )) {
                    retMap.put( "code", HttpServletResponse.SC_OK );
                    logger.info( "TOKEN验证处理结果----CODE=" + HttpServletResponse.SC_OK + ";TOKEN验证通过！" );
                } else {
                    redisUtils.remove( sessionId );
                    retMap.put( "code", HttpServletResponse.SC_UNAUTHORIZED );
                    logger.info( "TOKEN验证处理结果----CODE=" + HttpServletResponse.SC_UNAUTHORIZED + ";非法访问接口！" );
                }
            } catch (Exception e) {
                redisUtils.remove( sessionId );
                retMap.put( "code", HttpServletResponse.SC_FORBIDDEN);
                logger.info( "TOKEN验证处理结果----CODE="+ HttpServletResponse.SC_FORBIDDEN+";TOKEN解析出错！"+ e.getMessage().toString() );
            }
        } else {
            retMap.put( "code", HttpServletResponse.SC_EXPECTATION_FAILED );
            logger.info( "TOKEN验证处理结果----CODE="+ HttpServletResponse.SC_EXPECTATION_FAILED+";TOKEN为空！" );
        }
        retMap.put( "request",request );
        logger.info( "TOKEN验证处理结束");
        return retMap;
    }
}
